/**
 *  @author 侯庆峰, 15/11/18.
 *
 *  @name LTOperationManager_hpp
 *
 *  @brief LTOpearationManager_hpp is used for opeartion manager.
 *
 *  @copyright © 2015年 letv. All rights reserved.
 */
#include <unistd.h>
#include "LTOperationManager.h"
#include <pthread.h>
#include <time.h>

#define MAXURLLENGTH 2083 //url最大长度

CURLM *LTOperationManager::multiCurl = curl_multi_init();
LTOperationManager *LTOperationManager::operationManger = new LTOperationManager();

/**
 * 构造函数
 */
LTOperationManager::LTOperationManager()
: _cond()
, _cond_mutex()
, _queue_mutex()
, shareCurlParam(NULL)
{
    this->SetShareCurlData();
    this->CreateThread();
}

/**
 *  Get Pid
 */
inline pthread_t LTOperationManager::GetPid()
{
    return LTOperationManager::GetInstance()->pid;
}

/**
 *  创建线程
 */
void LTOperationManager::CreateThread()
{
    int state = pthread_create(&this->pid, NULL, CurlManger, this);
    
    if (state == -1) {
        cerr << "-------------ERROR WARNING------------" << endl;
        cerr << "create thread failed!!!!" << endl;
    }
}

/**
 *  设置curl共享数据
 */
void LTOperationManager::SetShareCurlData()
{
    shareCurlParam = curl_share_init();
    curl_share_setopt(shareCurlParam, CURLSHOPT_USERDATA, CURL_LOCK_DATA_DNS);//DNS共享
}

/**
 *  获得单例对象
 */
LTOperationManager *LTOperationManager::GetInstance()
{
    return operationManger;
}

/**
 * 创建operation
 */
LTHttpOperation *LTOperationManager::CreateOperation()
{
    LTHttpOperation *operation = new LTHttpOperation();
    
    if (!operation) {
        return  NULL;
    }
    
    return operation;
}

/**
 *  删除operation
 */
void LTOperationManager::DeleteOperation(LTHttpOperation *operation)
{
    if (!operation) {
        return;
    }
    
    operation->SetIsDeleted();
    this->RemoveOperation(operation);
}

/**
 * 管理curl操作
 */
void *LTOperationManager::CurlManger(void *pramater)
{
    if (!pramater) {
        return NULL;
    }
    
    LTOperationManager *manger = static_cast<LTOperationManager*>(pramater);
    manger->Monitor(manger);//监听连接
    return 0;
}

/**
 *  增加操作
 */
void LTOperationManager::AddOperation(LTHttpOperation *operation)
{
    if (!operation) {
        return;
    }
    
    {
        std::unique_lock <std::mutex> guard(_queue_mutex);
        _addqueue.push_back(operation);
    }
    
    {
        std::unique_lock <std::mutex> guard(_cond_mutex);
        _cond.notify_all();
    }
}
/**
 *  增加操作
 */
bool LTOperationManager::ExcuteOperation(LTHttpOperation *operation,string& err)
{
    if (!operation||!operation->GetCurl()) {
        return false;
    }
    int code = curl_easy_perform(operation->GetCurl());
    if (code == CURLE_OK) {
        err = "";
        return true;
    }else{
        err = curl_easy_strerror((CURLcode)code);
        return false;
    }
}
/**
 *  删除操作
 */
void LTOperationManager::RemoveOperation(LTHttpOperation *operation)
{
    if (!operation) {
        return;
    }
    
    {
        std::unique_lock <std::mutex> guard(_queue_mutex);
        do{
            for (vector<LTHttpOperation*>::iterator it = _addqueue.begin(); it!=_addqueue.end(); it++) {
                if ((*it)== operation) {
                    _addqueue.erase(it);
                    break;
                }
            }
            _delqueue.push_back(operation);
        }while(false);
    }
    {
        std::unique_lock <std::mutex> guard(_cond_mutex);
        _cond.notify_all();
    }
}

/**
 *  是否需要共享dns
 */
bool LTOperationManager::IsShareDNS(char *url)
{
    if (!url) {
        return false;
    }
    
    size_t length = strlen(url);//IE浏览器url最大字符长度2083
    if (length > MAXURLLENGTH) {
        return false;
    }
    
    char urlTemp[MAXURLLENGTH] = {0};
    int i = 0;
    while (i < length) {
        
        urlTemp[i++] = *url;
        if (*url == '/') {
            if(*(url + 1) == '/'){
                ++url;
                urlTemp[i++] = *url++;
                continue;
            } else {
                break;
            }
        }
        ++url;
    }
    
    if (!this->shareURL.empty()) {
        for(vector<char*>::iterator ite = this->shareURL.begin();
            ite != this->shareURL.end(); ++ite){
            if (strcmp(*ite, urlTemp) == 0) {
                return false;
            }
        }
    }
    
    this->shareURL.push_back(urlTemp);
    return true;
}

/**
 *  监听连接
 *
 *  @param multi_handle CURLM句柄
 */
void LTOperationManager::Monitor(LTOperationManager *manger)
{
    if (!manger) {
        return;
    }
    
    int still_running  = 0;
    int rc = 0;
    
    pthread_cleanup_push(CleanMemory, NULL);
    do
    {
        bool empty;
        {
            std::unique_lock <std::mutex> guard(_queue_mutex);
            empty = _addqueue.empty();
        }
        
        if (still_running == 0&&empty) {
            std::unique_lock <std::mutex> guard(_cond_mutex);
            _cond.wait(guard);
        }
        
        
        struct timeval timeout;
        fd_set fdread;
        fd_set fdwrite;
        fd_set fdexcep;
        int maxfd = -1;
        long curl_timeo = -1;
        
        FD_ZERO(&fdread);
        FD_ZERO(&fdwrite);
        FD_ZERO(&fdexcep);
        
        timeout.tv_sec = 1;
        timeout.tv_usec = 0;
        
        if (!_addqueue.empty()) {
            std::unique_lock <std::mutex> guard(_queue_mutex);
            for (vector<LTHttpOperation*>::iterator it = _addqueue.begin(); it!=_addqueue.end(); it++) {
                if (*it) {
                    operationMap.insert(make_pair((*it)->GetCurl(), (*it)));
                    
                    if (this->IsShareDNS((*it)->GetUrl())) {
                        curl_easy_setopt((*it)->GetCurl(), CURLOPT_SHARE, shareCurlParam);
                    }
                    
                    curl_multi_add_handle(LTOperationManager::multiCurl, (*it)->GetCurl());
                }
            }
            _addqueue.clear();
        }
        
        if (!_delqueue.empty()) {
            std::unique_lock <std::mutex> guard(_queue_mutex);
            for (vector<LTHttpOperation*>::iterator it = _delqueue.begin(); it != _delqueue.end(); it++) {
                map<CURL*, LTHttpOperation*>::iterator mapIte =operationMap.find((*it)->GetCurl());
                if (mapIte!=operationMap.end()) {
                    
                    if (mapIte->second && mapIte->second->GetCurl()) {
                        curl_multi_remove_handle(LTOperationManager::multiCurl, mapIte->first);
                        delete mapIte->second;
                        mapIte->second = NULL;
                    }
                    operationMap.erase(mapIte);
                }
            }
            _delqueue.clear();
        }
        
        curl_multi_perform(LTOperationManager::multiCurl, &still_running);
        curl_multi_timeout(LTOperationManager::multiCurl, &curl_timeo);
        
        if(curl_timeo >= 0) {
            timeout.tv_sec = curl_timeo / 1000;
            if(timeout.tv_sec > 1)
                timeout.tv_sec = 1;
            else
                timeout.tv_usec = (curl_timeo % 1000) * 1000;
        }
        
        curl_multi_fdset(LTOperationManager::multiCurl, &fdread, &fdwrite, &fdexcep, &maxfd);
        
        if(maxfd != -1) {
            rc = select(maxfd+1, &fdread, &fdwrite, &fdexcep, &timeout);
        }
        
        switch(rc) {
            case -1:
                still_running = 0;
                break;
            case 0:
            default:
                CompletionConnection();
                break;
        }
    }while (1);
    pthread_cleanup_pop(0);
}

/**
 *  连接完成，回调处理
 */
void LTOperationManager::CompletionConnection()
{
    if(operationMap.empty())
    {
        return;
    }
    
    struct CURLMsg *curlMsg;
    
    do {
        int completion = 0;
        curlMsg = curl_multi_info_read(LTOperationManager::multiCurl, &completion);
        if(curlMsg && (curlMsg->msg == CURLMSG_DONE)) {
            CURL *curl = curlMsg->easy_handle;
            
            if (curl == NULL) {
                continue;
            }
            
            map<CURL*, LTHttpOperation*>::iterator mapIte =operationMap.find(curl);
            if(mapIte!=operationMap.end())
            {
                if (!mapIte->second) {
                    continue;
                }
                
                if (mapIte->second->GetCurl() == curl) {
                    
                    char *returnData = NULL;
                    bool returnBool = true;
                    size_t returnDataLength = 0;
                    
                    if (!curlMsg->data.result){
                        returnData = mapIte->second->GetBufferData();
                        returnBool = true;
                        returnDataLength = mapIte->second->GetBufferDataLength();
                    } else {
                        returnData = (char*)curl_easy_strerror(curlMsg->data.result);
                        returnDataLength = strlen(returnData);
                        returnBool = false;
                    }
                    
                    if (!mapIte->second->GetIsDeleted()) {
                        if (mapIte->second->GetCallBack()) {
                            (mapIte->second->GetCallBack())((void*)returnData, returnDataLength,
                                                            returnBool, mapIte->second->GetContext());
                        }
                    }
                }
            }
        }
    } while(curlMsg);
}

/**
 *  Clean memory
 */
void LTOperationManager::CleanMemory(void *context)
{
    if (LTOperationManager::GetInstance()->_addqueue.empty()) {
        for (int i = 0; i < LTOperationManager::GetInstance()->_addqueue.size(); ++i){
            LTOperationManager::GetInstance()->_addqueue[i]->GetCallBack()
            (NULL, 0, false, LTOperationManager::GetInstance()->_addqueue[i]->GetContext());
            LTHttpOperation *operation = LTOperationManager::GetInstance()->_addqueue[i];
            if (operation) {
                delete operation;
                operation = NULL;
            }
        }
        LTOperationManager::GetInstance()->_addqueue.clear();
    }
    
    if (!LTOperationManager::GetInstance()->_delqueue.empty()) {
        for (int i = 0; i < LTOperationManager::GetInstance()->_delqueue.size(); ++i){
            LTOperationManager::GetInstance()->_delqueue[i]->GetCallBack()
            (NULL, 0, false, LTOperationManager::GetInstance()->_delqueue[i]->GetContext());
            LTHttpOperation *operation = LTOperationManager::GetInstance()->_addqueue[i];
            if (operation) {
                delete operation;
                operation = NULL;
            }
        }
        LTOperationManager::GetInstance()->_delqueue.clear();
    }
}

/**
 *  析构函数
 */
LTOperationManager::~LTOperationManager()
{
    if (pid) {
        int state = pthread_kill(pid,0);
        if (!state) {
            pthread_join(pid, NULL); //等待清理工作完成
        }
    }
    
    if (multiCurl) {
        curl_multi_cleanup(LTOperationManager::multiCurl);
        multiCurl = NULL;
    }
    
    if (shareCurlParam) {
        curl_share_cleanup(shareCurlParam);
        shareCurlParam = NULL;
    }
}
